/*

This file is part of Rooms3D.

Copyright (C) 2008 Pixecur Technologies Incorporated (Esotera).
Visit Esotera at http://www.esoteras3.com
 
Rooms3D is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
Rooms3D is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with Rooms3D; see the file COPYING.  If not, write to 
Pixecur Technologies, 213-31 Peet Street, St. John's, NL, A1B 3W8. 
 
Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
 
*/

package com.datasentinel.rooms.util.sh3d;

import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.util.Collection;

import javax.media.j3d.Transform3D;
import javax.vecmath.AxisAngle4f;
import javax.vecmath.Matrix3d;
import javax.vecmath.Matrix3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import com.eteks.sweethome3d.model.Camera;
import com.eteks.sweethome3d.model.Home;
import com.eteks.sweethome3d.model.HomePieceOfFurniture;
import com.eteks.sweethome3d.model.Wall;

public class FurnitureUtils
{
	private Home home;
	private Transform3D wallTransform;
	private AxisAngle4f wallRotation;
	private Vector3f wallTranslation;
	private Vector3f pieceOriginOffsetFromCentroid;
	private Transform3D pieceTransform;
	private Transform3D rotationTransform;
	private Point3f wallPoint;
	
	private Point3f newWallPickPoint;
	private Point3f oldWallPickPoint;
	private Point3f furniturePosition;
	private Vector3f furniturePositionOffset;

	public FurnitureUtils(Home newHome)
	{
		super();
		home = newHome;
		wallTransform = new Transform3D();
		wallRotation = new AxisAngle4f();
		wallTranslation = new Vector3f();
		pieceOriginOffsetFromCentroid = new Vector3f();
		pieceTransform = new Transform3D();
		wallPoint = new Point3f();
		
		newWallPickPoint = new Point3f();
		oldWallPickPoint = new Point3f();
		furniturePosition = new Point3f();
		furniturePositionOffset = new Vector3f();
		
		rotationTransform = new Transform3D();
	}

	/*
	 * Useful for manual layout (like drag and drop)
	 */
	public void placeFurnitureOnWall(HomePieceOfFurniture homePiece, Wall wall,
			Point3d wallPickPoint)
	{
		updateWallTransform3D(wall);
		flipYZ(wallPickPoint,wallPoint);		
		placeFurnitureOnWall(homePiece, wall, wallPoint);
	}
	
	/*
	 * Useful for automated layout (like a layout manager)
	 */
	public void placeFurnitureOnWall(HomePieceOfFurniture homePiece, Wall wall, float u, float v)
	{
		updateWallTransform3D(wall);
		wallPoint.set(u, wall.getThickness() + 1, v);
		wallTransform.transform(wallPoint);
		placeFurnitureOnWall(homePiece, wall, wallPoint);
	}

	private void placeFurnitureOnWall(HomePieceOfFurniture homePiece, Wall wall,
			Point3f wallPickPoint)
	{
		wallPoint.set(wallPickPoint);
		float y = homePiece.isDoorOrWindow()?-wall.getThickness()/2:homePiece.getDepth();
		pieceOriginOffsetFromCentroid.set(homePiece.getWidth()/2, y, -homePiece.getHeight());
		wallTransform.transform(pieceOriginOffsetFromCentroid);
		pieceTransform.setTranslation(pieceOriginOffsetFromCentroid);
		pieceTransform.transform(wallPoint);
		home.setPieceOfFurnitureAngle(homePiece, wallRotation.getAngle());
		home.setPieceOfFurnitureLocation(homePiece, wallPoint.x, wallPoint.y);
		home.setPieceOfFurnitureElevation(homePiece, wallPoint.z);
	}

	private void updateWallTransform3D(Wall wall)
	{
		wallRotation.setAngle(wallAngleWithRespectToXaxis(wall));
		wallTranslation.set(wall.getXStart(), wall.getYStart(), 0);
		wallTransform.setRotation(wallRotation);
		wallTransform.setTranslation(wallTranslation);
	}

	private float wallAngleWithRespectToXaxis(Wall wall)
	{
		float wvx = wall.getXEnd() - wall.getXStart();
		float wvy = wall.getYEnd() - wall.getYStart();
		float cosineOfWallAngleWithRespectToXaxis = wvx / (float)Math.sqrt(wvx * wvx + wvy * wvy);
		float wallAngleWithRespectToXaxis = (float)Math.acos(cosineOfWallAngleWithRespectToXaxis);
		if (cosineOfWallAngleWithRespectToXaxis < 0)
			wallAngleWithRespectToXaxis *= -1;
		if (wallAngleWithRespectToXaxis > 0 && wvy < 0)
			wallAngleWithRespectToXaxis *= -1;
		return wallAngleWithRespectToXaxis;
	}
	
	public void moveFurnitureOnWall(HomePieceOfFurniture homePiece, Wall wall,
			Point3d newWallPickPoint,Point3d oldWallPickPoint)
	{
		updateWallTransform3D(wall);
		flipYZ(newWallPickPoint, this.newWallPickPoint);
		flipYZ(oldWallPickPoint, this.oldWallPickPoint);
		_moveFurnitureOnWall(homePiece, wall, this.newWallPickPoint,this.oldWallPickPoint);
	}
	
	public void placeFurnitureOnFloor(HomePieceOfFurniture homePiece, Point3d groundPickPoint)
	{
		flipYZ(groundPickPoint,wallPoint);		
//		wallPoint.set(groundPickPoint);
//		if (homePiece.isDoorOrWindow())
//			pieceOriginOffsetFromCentroid.set(homePiece.getWidth() / 2, -wall.getThickness() / 2,
//				-homePiece.getHeight());
//		else
//			pieceOriginOffsetFromCentroid.set(homePiece.getWidth() / 2, homePiece.getDepth(),
//				-homePiece.getHeight());
//		wallTransform.transform(pieceOriginOffsetFromCentroid);
//		pieceTransform.setTranslation(pieceOriginOffsetFromCentroid);
		pieceTransform.transform(wallPoint);
		home.setPieceOfFurnitureAngle(homePiece, wallRotation.getAngle());
		home.setPieceOfFurnitureLocation(homePiece, wallPoint.x, wallPoint.y);
		home.setPieceOfFurnitureElevation(homePiece, wallPoint.z);
	}

	public void moveFurnitureOnFloor(HomePieceOfFurniture homePiece,
			Point3d newFloorPickPoint,Point3d oldFloorPickPoint)
	{
		flipYZ(newFloorPickPoint, this.newWallPickPoint);
		flipYZ(oldFloorPickPoint, this.oldWallPickPoint);
		_moveFurnitureOnFloor(homePiece, this.newWallPickPoint,this.oldWallPickPoint);
	}
	
	private void _moveFurnitureOnWall(HomePieceOfFurniture homePiece, Wall wall,
			Point3f newWallPickPoint,Point3f oldWallPickPoint)
	{
		furniturePosition.set(homePiece.getX(),homePiece.getY(),homePiece.getElevation());
		furniturePositionOffset.set(newWallPickPoint);
		furniturePositionOffset.sub(oldWallPickPoint);
		pieceTransform.setTranslation(furniturePositionOffset);
		pieceTransform.transform(furniturePosition);
		home.setPieceOfFurnitureAngle(homePiece, wallRotation.getAngle());
		home.setPieceOfFurnitureLocation(homePiece, furniturePosition.x, furniturePosition.y);
		boolean isDoor = homePiece.isDoorOrWindow() && homePiece.getName().toLowerCase().indexOf("door") >= 0;
		home.setPieceOfFurnitureElevation(homePiece, isDoor?0.0f:furniturePosition.z);
	}

	public Point3f[] getVertices(HomePieceOfFurniture homePiece)
	{
		float[][] modelRotation = homePiece.getModelRotation();
		Matrix3f modelRotationMatrix = new Matrix3f(modelRotation[0][0],
				modelRotation[0][1], modelRotation[0][2], modelRotation[1][0],
				modelRotation[1][1], modelRotation[1][2], modelRotation[2][0],
				modelRotation[2][1], modelRotation[2][2]);
		rotationTransform.setRotation(new Matrix3d(modelRotationMatrix));
		Point3f points[] = new Point3f[4];
		for(int i = 0; i < points.length; i++)
		{
			points[i] = new Point3f(homePiece.getX(),homePiece.getY(),homePiece.getElevation());
			switch (i)
			{
//				case 1:	points[i].x += homePiece.getWidth();											break;
//				case 2:	points[i].x += homePiece.getWidth();	points[i].y += homePiece.getHeight();	break;
//				case 3:	points[i].y += homePiece.getHeight();											break;
				case 1:	points[i].x += homePiece.getWidth();											break;
				case 2:	points[i].x += homePiece.getWidth();	points[i].y -= homePiece.getHeight();	break;
				case 3:	points[i].y -= homePiece.getHeight();											break;
			}
			rotationTransform.transform(points[i]);
		}
		return points;
	}
	
	private void _moveFurnitureOnFloor(HomePieceOfFurniture homePiece,
			Point3f newFloorPickPoint,Point3f oldFloorPickPoint)
	{
		furniturePosition.set(homePiece.getX(),homePiece.getY(),homePiece.getElevation());
		furniturePositionOffset.set(newFloorPickPoint);
		furniturePositionOffset.sub(oldFloorPickPoint);
		if (furniturePositionOffset.lengthSquared() > 100.0)
			furniturePositionOffset.scale(9.0f/furniturePositionOffset.length());
		pieceTransform.setTranslation(furniturePositionOffset);
		pieceTransform.transform(furniturePosition);
//		home.setPieceOfFurnitureAngle(homePiece, wallRotation.getAngle());
		home.setPieceOfFurnitureLocation(homePiece, furniturePosition.x, furniturePosition.y);
//		home.setPieceOfFurnitureElevation(homePiece, furniturePosition.z);
	}

	private void flipYZ(Point3d in,Point3f out)
	{
		float x = (float)in.getX();
		float y = (float)in.getZ();
		float z = (float)in.getY();
		out.set(x, y, z);
	}
}
